﻿using PBO.Game.BattleClass;
using PBO.Game.BattleLog;
using PokemonBattle.BattleNetwork;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Linq;

namespace PBO.Game.Img
{
    public class ScreenUpPainter : IDisposable
    {
        BattleGroup _MyGroup;
        BattleGroup MyGroup
        {
            get { return this._MyGroup; }
            set
            {
                if (_MyGroup != null)
                {
                    _MyGroup.PokemonChanged -= myTeam_PokemonChanged;
                    _MyGroup.PokemonDied -= myTeam_PokemonDied;
                    _MyGroup.PokemonHPChanged -= myTeam_PokemonStateChanged;
                    _MyGroup.PokemonStateChanged -= myTeam_PokemonStateChanged;
                    _MyGroup.PokemonImageChanged -= myTeam_PokemonImageChanged;
                }
                _MyGroup = value;
                if (_MyGroup != null)
                {
                    _MyGroup.PokemonChanged += myTeam_PokemonChanged;
                    _MyGroup.PokemonDied += myTeam_PokemonDied;
                    _MyGroup.PokemonHPChanged += myTeam_PokemonStateChanged;
                    _MyGroup.PokemonStateChanged += myTeam_PokemonStateChanged;
                    _MyGroup.PokemonImageChanged += myTeam_PokemonImageChanged;
                }
            }
        }
        BattleGroup _OpponentGroup;
        BattleGroup OpponentGroup
        {
            get { return this._OpponentGroup; }
            set
            {
                if (_OpponentGroup != null)
                {
                    _OpponentGroup.PokemonChanged -= targetTeam_PokemonChanged;
                    _OpponentGroup.PokemonDied -= targetTeam_PokemonDied;
                    _OpponentGroup.PokemonHPChanged -= targetTeam_PokemonStateChanged;
                    _OpponentGroup.PokemonStateChanged -= targetTeam_PokemonStateChanged;
                    _OpponentGroup.PokemonImageChanged -= targetTeam_PokemonImageChanged;
                }
                _OpponentGroup = value;
                if (_OpponentGroup != null)
                {
                    _OpponentGroup.PokemonChanged += targetTeam_PokemonChanged;
                    _OpponentGroup.PokemonDied += targetTeam_PokemonDied;
                    _OpponentGroup.PokemonHPChanged += targetTeam_PokemonStateChanged;
                    _OpponentGroup.PokemonStateChanged += targetTeam_PokemonStateChanged;
                    _OpponentGroup.PokemonImageChanged += targetTeam_PokemonImageChanged;
                }
            }
        }

        private BattleLogger logger;

        private Bitmap terrainImage;
        private List<Image> myPokemonState;

        private List<Image> opponentPokemonState;
        private Bitmap Image;
        private object imageSync;
        private PictureBox picScreen;

        public ScreenUpPainter(PictureBox picturebox1, BattleTerrain terrain, BattleLogger logger, BattleGroup teamValue, BattleGroup targetTeamValue, BattleMode mode)
        {
            picScreen = picturebox1;
            terrainImage = ImageHelper.GetTerrainImage(terrain);
            this.logger = logger;
#warning logger is unused
            MyGroup = teamValue;
            OpponentGroup = targetTeamValue;
            myPokemonState = new List<Image>();
            opponentPokemonState = new List<Image>();
            UpdateRects(mode);

            for (int i = 1; i <= MyGroup.SelectedPokemon.Count(); i++)
            {
                myPokemonState.Add(null);
                UpdatePokemonState(i);
                opponentPokemonState.Add(null);
                UpdatePokemonState2(i);
            }
            picScreen.Image = new Bitmap(256, 144);
            Image = (Bitmap)picScreen.Image;
            imageSync = new object();
        }

        public void Reset()
        {
            picScreen.Image = new Bitmap(256, 144);
            Image = (Bitmap)picScreen.Image;
            Paint();
        }
        public void Paint()
        {
            Draw(terrainImage, new Rectangle(0, 0, 256, 144));

            for (int i = 1; i <= myPokemonState.Count; i++)
            {
                DrawMyPokemonState(i);
                DrawOpponentPokemonState(i);

                var list1 = MyGroup.SelectedPokemon.ToArray();
                var list2 = OpponentGroup.SelectedPokemon.ToArray();
                for (int j = 0; j < list1.Length; j++)
                {
                    if (list1[j].HealthPower > 0 && !list1[j].BattleState.Hide)
                    {
                        DrawAndClipped(list1[j].BackImage, MyPokemonRects[j]);
                    }
                }
                for (int j = 0; j < list2.Length; j++)
                {
                    if (list2[j].HealthPower > 0 && !list2[j].BattleState.Hide)
                    {
                        DrawAndClipped(list2[j].FrontImage, OpponentPokemonRects[j]);
                    }
                }
                //for (int i2 = 0; i2 <= myTeam.SelectedPokemon.Count - 1; i2++)
                //{
                //    Pokemon pm = myTeam.SelectedPokemon[i2];
                //    if (pm.HealthPower != 0 && !pm.BattleState.Hide)
                //        DrawAndClipped(pm.BackImage, MyPokemonRects[i2]);
                //}

                //for (int i3 = 0; i3 <= opponentTeam.SelectedPokemon.Count - 1; i3++)
                //{
                //    Pokemon pm = opponentTeam.SelectedPokemon[i3];
                //    if (pm.HealthPower != 0 && !pm.BattleState.Hide)
                //    {
                //        DrawAndClipped(pm.FrontImage, OpponentPokemonRects[i3]);
                //    }
                //}
            }
            Update();
        }

        private void UpdatePokemonState(int index)
        {
            var pm = MyGroup.SelectedPokemon.ElementAt(index - 1);

            if (MyPokemonStateRects.Count == 1 && index == 1)
            {
                myPokemonState[index - 1] = ImageHelper.GetPMStateImg(1, pm, true);
            }
            else
            {
                myPokemonState[index - 1] = ImageHelper.GetPMStateImg(1, pm);
            }
        }
        private void RedrawMyPokemon(int drawDiedIndex = 0)
        {
            Clear(MyPokemonRects);
            DrawMyPokemonWithoutClear(drawDiedIndex);
        }
        private void DrawMyPokemonWithoutClear(int drawDiedIndex = 0)
        {
            var arr = MyGroup.SelectedPokemon.ToArray();
            for (int i = 0; i < arr.Length; i++)
            {
                Pokemon pm = arr[i];
                if (drawDiedIndex == i + 1)
                {
                    DrawAndClipped(pm.BackImage, MyPokemonRects[i]);
                }
                else if (pm.HealthPower != 0 && !pm.BattleState.Hide)
                {
                    DrawAndClipped(pm.BackImage, MyPokemonRects[i]);
                }
            }
            //for (int i = 0; i <= myTeam.SelectedPokemon.Count - 1; i++)
            //{
            //    Pokemon pm = myTeam.SelectedPokemon[i];
            //    if (drawDiedIndex == i + 1)
            //    {
            //        DrawAndClipped(pm.BackImage, MyPokemonRects[i]);
            //    }
            //    else if (pm.HealthPower != 0 && !pm.BattleState.Hide)
            //    {
            //        DrawAndClipped(pm.BackImage, MyPokemonRects[i]);
            //    }
            //}
            Update();
        }
        private void DrawMyPokemonState(int index)
        {
            if (myPokemonState[index - 1] != null)
                Draw(myPokemonState[index - 1], MyPokemonStateRects[index - 1]);
        }

        private void UpdatePokemonState2(int index)
        {
            var pm = OpponentGroup.SelectedPokemon.ElementAt(index - 1);
            if (OpponentPokemonStateRects.Count == 1 && index == 1)
            {
                opponentPokemonState[index - 1] = ImageHelper.GetPMStateImg(2, pm, true);
            }
            else
            {
                opponentPokemonState[index - 1] = ImageHelper.GetPMStateImg(2, pm);
            }
        }
        private void RedrawOpponentPokemon(int drawDiedIndex = 0, int drawFrameIndex = 0)
        {
            Clear(OpponentPokemonRects);
            DrawOpponentPokemonWithoutClear(drawDiedIndex, drawFrameIndex);
        }

        private void DrawOpponentPokemonWithoutClear(int drawDiedIndex = 0, int drawFrameIndex = 0)
        {
            var arr = OpponentGroup.SelectedPokemon.ToArray();
            for (int i = 0; i < arr.Length; i++)
            {
                Pokemon pm = arr[i];
                if (drawDiedIndex == i + 1)
                {
                    DrawAndClipped(pm.FrontImage, OpponentPokemonRects[i]);
                }
                else if (drawFrameIndex == i + 1)
                {
                    DrawAndClipped(pm.Frame, OpponentPokemonRects[i]);
                }
                else if (pm.HealthPower != 0 && !pm.BattleState.Hide)
                {
                    DrawAndClipped(pm.FrontImage, OpponentPokemonRects[i]);
                }
            }
            //for (int i = 0; i < opponentTeam.SelectedPokemon.Count; i++)
            //{
            //    Pokemon pm = opponentTeam.SelectedPokemon[i];
            //    if (drawDiedIndex == i + 1)
            //    {
            //        DrawAndClipped(pm.FrontImage, OpponentPokemonRects[i]);
            //    }
            //    else if (drawFrameIndex == i + 1)
            //    {
            //        DrawAndClipped(pm.Frame, OpponentPokemonRects[i]);
            //    }
            //    else if (pm.HealthPower != 0 && !pm.BattleState.Hide)
            //    {
            //        DrawAndClipped(pm.FrontImage, OpponentPokemonRects[i]);
            //    }
            //}
            Update();
        }
        private void DrawOpponentPokemonState(int index)
        {
            if (opponentPokemonState[index - 1] != null)
                Draw(opponentPokemonState[index - 1], OpponentPokemonStateRects[index - 1]);
        }

        #region changed

        private void myTeam_PokemonChanged(object pokemon, System.EventArgs e)
        {
            int index = GetPokemonIndex((Pokemon)pokemon);
            UpdatePokemonState(index);
            DrawMyPokemonState(index);
            Pokemon pm = (Pokemon)pokemon;

            Clear(MyPokemonRects[index - 1]);
            if (!pm.BattleState.substituted)
            {
                Rectangle tmpRect = MyPokemonRects[index - 1];
                for (int i = 1; i <= 4; i++)
                {
                    if (i % 2 == 1)
                    {
                        MyPokemonRects[index - 1] = new Rectangle(new Point(tmpRect.X - 3, tmpRect.Y), new Size(80, 80));
                    }
                    else
                    {
                        MyPokemonRects[index - 1] = new Rectangle(new Point(tmpRect.X + 3, tmpRect.Y), new Size(80, 80));
                    }
                    DrawMyPokemonWithoutClear();
                    System.Threading.Thread.Sleep(150);
                    Clear(MyPokemonRects[index - 1]);
                }
                MyPokemonRects[index - 1] = tmpRect;
            }
            RedrawMyPokemon();
        }
        private void myTeam_PokemonDied(object Pokemon, EventArgs e)
        {
            int index = GetPokemonIndex((Pokemon)Pokemon);
            Clear(MyPokemonStateRects[index - 1]);
            if (!((Pokemon)Pokemon).BattleState.Hide)
            {
                Rectangle tmpRect = MyPokemonRects[index - 1];
                for (int i = 1; i <= 10; i++)
                {
                    Clear(MyPokemonRects[index - 1]);
                    MyPokemonRects[index - 1] = new Rectangle(new Point(tmpRect.X, tmpRect.Y + 8 * i), new Size(80, 80 - 8 * i));
                    DrawMyPokemonWithoutClear(index);
                    System.Threading.Thread.Sleep(100);
                }
                MyPokemonRects[index - 1] = tmpRect;
            }
            RedrawMyPokemon();
        }
        private void myTeam_PokemonStateChanged(object pokemon, EventArgs e)
        {
            int index = GetPokemonIndex((Pokemon)pokemon);
            UpdatePokemonState(index);
            DrawMyPokemonState(index);
            Update();
        }
        private void myTeam_PokemonImageChanged(object pokemon, EventArgs e)
        {
            int index = GetPokemonIndex((Pokemon)pokemon);

            RedrawMyPokemon();
        }
        private void targetTeam_PokemonChanged(object pokemon, EventArgs e)
        {
            int index = GetPokemonIndex((Pokemon)pokemon);
            UpdatePokemonState2(index);
            DrawOpponentPokemonState(index);
            Pokemon pm = (Pokemon)pokemon;
            Clear(OpponentPokemonRects[index - 1]);

            if (!pm.BattleState.substituted)
            {
                Rectangle tmpRect = OpponentPokemonRects[index - 1];
                for (int i = 1; i <= 4; i++)
                {
                    if (i % 2 == 1)
                    {
                        OpponentPokemonRects[index - 1] = new Rectangle(new Point(tmpRect.X - 3, tmpRect.Y), new Size(80, 80));
                    }
                    else
                    {
                        OpponentPokemonRects[index - 1] = new Rectangle(new Point(tmpRect.X + 3, tmpRect.Y), new Size(80, 80));
                    }
                    DrawOpponentPokemonWithoutClear(0, index);
                    System.Threading.Thread.Sleep(150);
                    Clear(OpponentPokemonRects[index - 1]);
                }
                OpponentPokemonRects[index - 1] = tmpRect;
            }
            RedrawOpponentPokemon();
        }
        private void targetTeam_PokemonDied(object pokemon, EventArgs e)
        {
            int index = GetPokemonIndex((Pokemon)pokemon);
            Clear(OpponentPokemonStateRects[index - 1]);
            if (!((Pokemon)pokemon).BattleState.Hide)
            {
                Rectangle tmpRect = OpponentPokemonRects[index - 1];
                for (int i = 1; i <= 10; i++)
                {
                    Clear(OpponentPokemonRects[index - 1]);
                    OpponentPokemonRects[index - 1] = new Rectangle(new Point(tmpRect.X, tmpRect.Y + 8 * i), new Size(80, 80 - 8 * i));
                    DrawOpponentPokemonWithoutClear(index);
                    System.Threading.Thread.Sleep(100);
                }
                OpponentPokemonRects[index - 1] = tmpRect;
            }
            RedrawOpponentPokemon();
        }
        private void targetTeam_PokemonStateChanged(object pokemon, EventArgs e)
        {
            int index = GetPokemonIndex((Pokemon)pokemon);
            UpdatePokemonState2(index);
            DrawOpponentPokemonState(index);
            Update();
        }
        private void targetTeam_PokemonImageChanged(object pokemon, EventArgs e)
        {
            int index = GetPokemonIndex((Pokemon)pokemon);
            RedrawOpponentPokemon();
        }

        #endregion

        private void Clear(List<Rectangle> rects)
        {
            rects.ForEach(Clear);
        }

        private void Clear(Rectangle rect)
        {
            if (rect.X < 0) { rect.Width += rect.X; rect.X = 0; }
            if (rect.X + rect.Width > 256)
                rect.Width = 256 - rect.X;
            if (rect.Y < 0) { rect.Height += rect.Y; rect.Y = 0; }
            if (rect.Y + rect.Height > 144)
                rect.Height = 144 - rect.Y;
            try
            {
                Image image = CloneTerrain(rect);
                Draw(image, rect);
            }
            catch (Exception ex)
            {
            }
        }

        private void Draw(Image img, Rectangle rect)
        {
            lock (imageSync)
            {
                try
                {
                    Graphics graph = Graphics.FromImage(Image);
                    try
                    {
                        graph.DrawImageUnscaled(img, rect);
                    }
                    finally
                    {
                        graph.Dispose();
                    }
                }
                catch (Exception ex)
                {
                }
            }
        }

        private void DrawAndClipped(Image img, Rectangle rect)
        {
            lock (imageSync)
            {
                try
                {
                    Graphics graph = Graphics.FromImage(Image);
                    try
                    {
                        graph.DrawImageUnscaledAndClipped(img, rect);
                    }
                    catch (Exception ex)
                    {
                    }
                    finally
                    {
                        graph.Dispose();
                    }
                }
                catch (Exception ex)
                {
                }
            }
        }

        private void Update()
        {
            if (picScreen.InvokeRequired)
            {
                picScreen.Invoke(new Action(Update));
            }
            else
            {
                picScreen.Refresh();
            }
        }

        private int GetPokemonIndex(Pokemon pm)
        {
            var list = pm.MyTeam.Group.SelectedPokemon.ToList();
            if (!list.Contains(pm)) return 0;
            return list.IndexOf(pm) + 1;
            //for (int i = 1; i <= opponentTeam.SelectedPokemon.Count; i++)
            //{
            //    if (object.ReferenceEquals(pm, myTeam.SelectedPokemon[i - 1]) || object.ReferenceEquals(pm, opponentTeam.SelectedPokemon[i - 1]))
            //        return i;
            //}
            //return 0;
        }

        private bool shaking;
        private int shakeIndex;
        private Point tmpPoint;
        private object locker = new object();
        public void StartShake(Pokemon pm)
        {
            if (shakeIndex != 0)
                StopShake();
            ThreadPool.QueueUserWorkItem(Shake, pm);
        }
        public void StopShake()
        {
            if (shakeIndex != 0)
            {
                shaking = false;
            }
        }
        private void Shake(object obj)
        {
            var pm = (Pokemon)obj;
            lock (locker)
            {
                int pokemonIndex = pm.MyTeam.Group.SelectedPokemon.ToList().IndexOf(pm) + 1;
                shakeIndex = pokemonIndex;
                tmpPoint = MyPokemonRects[shakeIndex - 1].Location;
                shaking = true;
                do
                {
                    Clear(MyPokemonRects[shakeIndex - 1]);
                    if (tmpPoint == MyPokemonRects[shakeIndex - 1].Location)
                    {
                        MyPokemonRects[shakeIndex - 1] = new Rectangle(tmpPoint.X, tmpPoint.Y + 1, 80, 80);
                    }
                    else
                    {
                        MyPokemonRects[shakeIndex - 1] = new Rectangle(tmpPoint, new Size(80, 80));
                    }
                    DrawMyPokemonWithoutClear();
                    if (!shaking)
                        break; // TODO: might not be correct. Was : Exit Do
                    Thread.Sleep(500);
                } while (shaking);
                MyPokemonRects[shakeIndex - 1] = new Rectangle(tmpPoint, new Size(80, 80));
                shakeIndex = 0;
                RedrawMyPokemon();
            }
        }

        private Dictionary<Rectangle, Bitmap> clearPool;
        private Bitmap CloneTerrain(Rectangle rect)
        {
            if (clearPool == null)
                clearPool = new Dictionary<Rectangle, Bitmap>();
            if (!clearPool.ContainsKey(rect))
            {
                Bitmap img = terrainImage.Clone(rect, terrainImage.PixelFormat);
                clearPool.Add(rect, img);
            }
            return clearPool[rect];
        }

        private List<Rectangle> MyPokemonRects;
        private List<Rectangle> MyPokemonStateRects;
        private List<Rectangle> OpponentPokemonRects;
        private List<Rectangle> OpponentPokemonStateRects;
        private void UpdateRects(BattleMode battleMode)
        {
            MyPokemonRects = new List<Rectangle>();
            MyPokemonStateRects = new List<Rectangle>();
            OpponentPokemonRects = new List<Rectangle>();
            OpponentPokemonStateRects = new List<Rectangle>();
            if (battleMode == BattleMode.Single)
            {
                MyPokemonRects.Add(new Rectangle(20, 64, 80, 80));
                MyPokemonStateRects.Add(new Rectangle(136, 95, 120, 38));

                OpponentPokemonRects.Add(new Rectangle(155, 13, 80, 80));
                OpponentPokemonStateRects.Add(new Rectangle(-6, 27, 126, 30));
            }
            else
            {
                MyPokemonRects.Add(new Rectangle(0, 64, 80, 80));
                MyPokemonRects.Add(new Rectangle(47, 64, 80, 80));
                MyPokemonStateRects.Add(new Rectangle(130, 87, 126, 30));
                MyPokemonStateRects.Add(new Rectangle(136, 116, 126, 30));

                OpponentPokemonRects.Add(new Rectangle(129, 3, 80, 80));
                OpponentPokemonRects.Add(new Rectangle(175, 7, 80, 80));
                OpponentPokemonStateRects.Add(new Rectangle(0, 0, 126, 30));
                OpponentPokemonStateRects.Add(new Rectangle(-6, 29, 126, 30));
            }
        }

        #region IDisposable

        // 检测冗余的调用
        private bool disposedValue = false;

        // IDisposable
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposedValue)
            {
                if (disposing)
                {
                    StopShake();
                    if (clearPool != null)
                        clearPool.Clear();

                }
            }
            this.disposedValue = true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion

    }
}
